package net.atomarrow.db.orm;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import net.atomarrow.db.annotation.FieldType;
import net.atomarrow.db.annotation.NotCreate;
import net.atomarrow.domains.Domain;
import net.atomarrow.domains.DomainUtil;
import net.atomarrow.exception.OrmConfigNotLoadException;

/**
 * orm控制器
 * @author Michael
 *
 */
public class OrmContext {
	private static final HashMap<String, DomainBean> tableMapContext = new HashMap<String, DomainBean>();
	private static final List<String> domainNames = new ArrayList<String>();



	private static HashMap<String, DomainBean> getContext() {
		return tableMapContext;
	}

	public static List<String> getAllDomainNames(){
	    return domainNames;
	}
	public static List<String> getMultipleTableDomainNames(){
	    List<String> mulits=new ArrayList<String>();
	    for(String domainName:domainNames){
	        if(DomainUtil.isMultiTable(domainName)){
	            mulits.add(domainName);
	        }
	    }
	    return mulits;
	}
	public static List<String> getSingleTableDomainNames(){
        List<String> singles=new ArrayList<String>();
        for(String domainName:domainNames){
            if(DomainUtil.isSingleTable(domainName)){
				Class<?> clazz = getDomainClass(domainName);
				if (clazz.isAssignableFrom(Domain.class)) {
					singles.add(domainName);
				}
            }
        }
        return singles;
    }

	public static void addDomainName(String domainName){
		if (domainNames.contains(domainName) == false) {
			domainNames.add(domainName);
		}
	}

	public static Class<?> getDomainClass(String tableName){
		DomainBean d = getDomain(tableName);
		if (d == null) {
			throw new OrmConfigNotLoadException("orm config not load:"+tableName);
		}
		return d.clazz;
	}
	private static DomainBean getDomain(String domainName) {
		return getContext().get(DomainUtil.getMappingName(domainName));
	}

	public static List<Attribute> getAttributes(String tableName) {
		DomainBean d = getDomain(tableName);
		if (d == null) {
			throw new OrmConfigNotLoadException("orm config not load:"+tableName);
		}
		return d.attributes;
	}

	public static void addDomain(Class<?> clazz) {
		addDomain(clazz.getSimpleName(), clazz);
	}

	public static void addBean(Class<?> clazz) {
		addDomain(clazz.getSimpleName(), clazz);
	}

	/**
	 * @param domainName
	 * @param clazz
	 */
	public static void addDomain(String domainName, Class<?> clazz) {

		String mappingName = DomainUtil.getMappingName(domainName);
		if(existName(mappingName)){
			throw new RuntimeException(domainName+"‘s mappingName "+mappingName+" conflict error");
		}
		addDomainName(clazz.getSimpleName());
		ArrayList<Attribute> attributes = new ArrayList<Attribute>();
		Field[] fields = clazz.getDeclaredFields();
		for (Field f : fields) {
			attributes.add(new Attribute(f.getName(), getSqlType(f),!f.isAnnotationPresent(NotCreate.class)));
		}
		tableMapContext.put(DomainUtil.getMappingName(domainName), new DomainBean(clazz, attributes));

	}

	public static void removeDomain(String domainName){
		String mappingName = DomainUtil.getMappingName(domainName);
		tableMapContext.remove(mappingName);
	}

	public static void reloadDomain(String domainName,Class<?> clazz){
		removeDomain(domainName);
		addDomain(domainName, clazz);
	}
	public static boolean existName(String mappingName) {
		return tableMapContext.containsKey(mappingName);
	}

	private static int getSqlType(Field f) {
		if (f.isAnnotationPresent(FieldType.class)) {
			FieldType type = f.getAnnotation(FieldType.class);
			switch (type.type()) {
			case VARCHAR:
				return Types.VARCHAR;
			case TEXT:
				return Types.VARCHAR;
			case DATE:
				return Types.DATE;
			case DATETIME:
			    return Types.TIMESTAMP;
			case TIME:
				return Types.TIME;
            default:
                break;
			}
		}

		Type type = f.getGenericType();
		if (type == String.class) {
			return Types.VARCHAR;
		} else if (type == Integer.TYPE || type == Integer.class) {
			return Types.INTEGER;
		} else if (type == Double.TYPE || type == Double.class) {
			return Types.DOUBLE;
		} else if (type == Date.class || type == java.sql.Date.class) {
			return Types.DATE;
		} else if (type == Timestamp.class) {
            return Types.TIMESTAMP;
        } else if (type == Boolean.TYPE || type == Boolean.class) {
			return Types.BIT;
		}else if (type == Long.TYPE || type == Long.class) {
            return Types.LONGNVARCHAR;
        }
		return 0;
	}
	public static void loadOrm(List<String> paths) {
        Class<?> clazz;
        try {
            for (String path : paths) {
                clazz = Class.forName(path);
                if(Domain.class.isAssignableFrom(clazz)){
                    addDomain(clazz.getSimpleName(), clazz);

                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
	}
}


class DomainBean {
	public DomainBean(Class<?> clazz, ArrayList<Attribute> attributes) {
		this.clazz = clazz;
		this.attributes = attributes;
	}

	Class<?> clazz;
	List<Attribute> attributes;
}
